import shap
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import os
def SHAP_instance(train_df,test_df,instance_index,rf,csv_pic_file=False):
    X_train = train_df.iloc[:, 2:]
    X_test = test_df.iloc[:, 2:]
    feature_names = list(X_train.columns)
    # 使用shap解释器
    explainer = shap.TreeExplainer(rf)
    shap_values = explainer.shap_values(X_test)
    # instance_index = 0
    # 获取真实类别
    y_true, y_special = test_df.iloc[instance_index, [1, 0]]

    # 获取预测概率
    y_pred_proba = rf.predict_proba(X_test)[instance_index]
    # print(f"Predicted probability: {y_pred_proba}")

    # 获取预测类别
    y_pred_class = y_pred_proba.argmax()
    # print(f"Predicted class: {y_pred_class}")
    # SHAP解释
    # expected_value = explainer.expected_value  #模型在训练数据集上的整体表现，预测各个类别的概率的平均值
    # print(f"Expected value: {expected_value}")  
    shap.summary_plot(shap_values, X_test, feature_names=feature_names)

    feature_importance = np.abs(shap_values).mean(axis=0)
    feature_importance_df = pd.DataFrame({
        'Feature': X_train.columns,
        'Importance': feature_importance
    }).sort_values(by='Importance', ascending=False)

    plt.figure(figsize=(10, 6))
    plt.barh(feature_importance_df['Feature'], feature_importance_df['Importance'])
    plt.xlabel('Mean SHAP Importance')
    plt.title('Feature Importance based on SHAP values')
    plt.show()

    
    if csv_pic_file is not False:
        instance_shap_values = shap_values[instance_index]  #特定实例的SHAP值
        instance_features = X_test.iloc[instance_index]  #特定实例的特征值
        # 创建一个 DataFrame 来存储 SHAP 值和特征
        shap_importance_df = pd.DataFrame({
            'Feature': instance_features.index,
            'SHAP Value': instance_shap_values
        })
        # 按照 SHAP 值排序
        shap_importance_df = shap_importance_df.sort_values(by='SHAP Value', ascending=True)
        # 准备其他列并填充 NaN 仅在你有数据时
        index_value = [instance_index] * len(shap_importance_df)  # 与特征数量相同的索引列表
        true_class_value = [y_true] * len(shap_importance_df)      # 真实类别
        special_class_value = [y_special] * len(shap_importance_df) # 特殊类别
        predicted_proba_value = [y_pred_proba] * len(shap_importance_df) # 预测概率
        predicted_class_value = [y_pred_class] * len(shap_importance_df)   # 预测分类
        # 如果其他列较短，可以填充空值
        instance_df = pd.DataFrame({
            'Index': index_value,
            'True_Class': true_class_value,
            'Special_Class': special_class_value,
            'Predicted_Probability': predicted_proba_value,
            'Predicted_Class': predicted_class_value,
            'Feature': shap_importance_df['Feature'],
            'SHAP Value': shap_importance_df['SHAP Value']
        })
        instance_df.loc[1:, ['Index', 'True_Class', 'Special_Class', 'Predicted_Probability', 'Predicted_Class']] = np.nan
        # 保存为csv文件
        dir = f'./SHAP_instance/sample_{instance_index}'
        instance_csv_file = os.path.join(dir, f'instance_df_{instance_index}.csv')
        if not os.path.exists(dir):
            os.makedirs(dir)
        instance_df.to_csv(instance_csv_file, index=False)  # 以不带索引的形式保存

        # 绘制条状图
        plt.figure(figsize=(10, 6))
        plt.barh(shap_importance_df['Feature'], shap_importance_df['SHAP Value'], color='skyblue')
        plt.xlabel('SHAP Value')
        plt.title(f'SHAP Values for Instance {instance_features}')
        plt.axvline(0, color='black', linewidth=0.8, linestyle='--')  # 添加垂直线以指示零值

        # 保存图片
        plt.savefig(os.path.join(dir, f'shap_values_instance_{instance_index}.png'), bbox_inches='tight')
        plt.close()  # 关闭图形以释放内存